home *** CD-ROM | disk | FTP | other *** search
/ Tech Arsenal 1 / Tech Arsenal (Arsenal Computer).ISO / tek-04 / ms_sh21s.zip / SH210 / SRC / SH1.C < prev    next >
C/C++ Source or Header  |  1992-12-14  |  56KB  |  2,733 lines

  1. /* MS-DOS SHELL - Main program, memory and variable management
  2.  *
  3.  * MS-DOS SHELL - Copyright (c) 1990,1,2 Data Logic Limited and Charles Forsyth
  4.  *
  5.  * This code is based on (in part) the shell program written by Charles
  6.  * Forsyth and is subject to the following copyright restrictions:
  7.  *
  8.  * 1.  Redistribution and use in source and binary forms are permitted
  9.  *     provided that the above copyright notice is duplicated in the
  10.  *     source form and the copyright notice in file sh6.c is displayed
  11.  *     on entry to the program.
  12.  *
  13.  * 2.  The sources (or parts thereof) or objects generated from the sources
  14.  *     (or parts of sources) cannot be sold under any circumstances.
  15.  *
  16.  *    $Header: /usr/users/istewart/src/shell/sh2.1/RCS/sh1.c,v 2.5 1992/12/14 10:54:56 istewart Exp $
  17.  *
  18.  *    $Log: sh1.c,v $
  19.  *    Revision 2.5  1992/12/14  10:54:56  istewart
  20.  *    BETA 215 Fixes and 2.1 Release
  21.  *
  22.  *    Revision 2.4  1992/11/06  10:03:44  istewart
  23.  *    214 Beta test updates
  24.  *
  25.  *    Revision 2.3  1992/09/03  18:54:45  istewart
  26.  *    Beta 213 Updates
  27.  *
  28.  *    Revision 2.2  1992/07/16  14:33:34  istewart
  29.  *    Beta 212 Baseline
  30.  *
  31.  *    Revision 2.1  1992/07/10  10:52:48  istewart
  32.  *    211 Beta updates
  33.  *
  34.  *    Revision 2.0  1992/04/13  17:39:09  Ian_Stewartson
  35.  *    MS-Shell 2.0 Baseline release
  36.  */
  37.  
  38. #include <sys/types.h>
  39. #include <sys/stat.h>
  40. #include <stdio.h>
  41. #include <stdlib.h>
  42. #include <signal.h>
  43. #include <errno.h>
  44. #include <setjmp.h>
  45. #include <stdarg.h>
  46. #include <string.h>
  47. #include <unistd.h>
  48. #include <ctype.h>
  49. #include <fcntl.h>
  50. #include <limits.h>
  51. #include <dirent.h>
  52.  
  53. /* OS2 or DOS, DEBUG MEMORY or normal malloc */
  54.  
  55. #ifdef OS2
  56. #  define INCL_DOSSESMGR
  57. #  define INCL_DOSMEMMGR
  58. #  define INCL_DOSPROCESS
  59. #  define INCL_WINSWITCHLIST
  60. #  include <os2.h>
  61.  
  62. #  ifdef OS2_DOSALLOC
  63. #    ifdef DEBUG_MEMORY
  64. #      define RELEASE_MEMORY(x)    if (DosFreeSeg (SELECTOROF ((x))))    \
  65.                     fprintf(stderr, "Memory Release error\n");
  66. #    else
  67. #      define RELEASE_MEMORY(x)    DosFreeSeg (SELECTOROF ((x)))
  68. #    endif
  69.  
  70. #  else
  71. #    define RELEASE_MEMORY(x)    free ((x))
  72. #  endif
  73.  
  74. #else
  75.  
  76. #  include <dos.h>
  77. #  define RELEASE_MEMORY(x)     free ((x))
  78. #endif
  79.  
  80. #include <time.h>
  81. #include "sh.h"
  82.  
  83. /*
  84.  * Structure of Malloced space to allow release of space nolonger required
  85.  * without having to know about it.
  86.  */
  87.  
  88. typedef struct region {
  89.     struct region    *next;
  90.     int            area;
  91. #ifdef DEBUG_MEMORY
  92.     size_t        nbytes;
  93. #endif
  94. } s_region;
  95.  
  96. static struct region    *MemoryAreaHeader = (s_region *)NULL;
  97.  
  98. /*
  99.  * default shell, search rules
  100.  */
  101.  
  102. static char        *shellname = "c:/bin/sh";
  103. static char        search[] = ";c:/bin;c:/usr/bin";
  104. static char        *ymail = "You have mail\n";
  105. static char        *ShellNameLiteral = "sh";
  106. static char        *tilde = "~";
  107. static char        LIT_OSmode[] = "OSMODE";
  108. static char        LIT_Dollar[] = "$";
  109. static char    *NOExit = "sh: ignoring attempt to leave lowest level shell\n";
  110. static bool        ProcessingDEBUGTrap = FALSE;
  111. static bool        ProcessingERRORTrap = FALSE;
  112. static unsigned char    ATOE_GFlags;    /* Twalk GLOBAL flags        */
  113. static unsigned char    ATNE_Function;    /* Twalk GLOBAL flags        */
  114.  
  115. /* Integer Variables */
  116.  
  117. static struct ShellVariablesInit {
  118.     char    *Name;            /* Name of variable        */
  119.     int        Status;            /* Initial status        */
  120.     char    *CValue;
  121. } InitialiseShellVariables[] = {
  122.     { LIT_COLUMNS,        STATUS_INTEGER,        "80"        },
  123.     { HistorySizeVariable,    STATUS_INTEGER,        "100"        },
  124.     { LineCountVariable,    STATUS_INTEGER,        "1"        },
  125.     { LIT_LINES,        STATUS_INTEGER,        "25"        },
  126.     { OptIndVariable,        STATUS_INTEGER,        "1"        },
  127.     { RandomVariable,        (STATUS_EXPORT | STATUS_INTEGER),
  128.                             null        },
  129.     { SecondsVariable,        (STATUS_EXPORT | STATUS_INTEGER),
  130.                             null        },
  131.     { LIT_OSmode,        (STATUS_EXPORT | STATUS_CANNOT_UNSET |
  132.                  STATUS_INTEGER),    null        },
  133.     { LIT_Dollar,        (STATUS_CANNOT_UNSET | STATUS_INTEGER),
  134.                             null        },
  135.  
  136.     { LastWordVariable,        STATUS_EXPORT,        null        },
  137.     { PathLiteral,        (STATUS_EXPORT | STATUS_CANNOT_UNSET),
  138.                             search        },
  139.     { IFS,            (STATUS_EXPORT | STATUS_CANNOT_UNSET),
  140.                             " \t\n"        },
  141.     { PS1,            (STATUS_EXPORT | STATUS_CANNOT_UNSET),
  142.                             "%e$ "        },
  143.     { PS2,            (STATUS_EXPORT | STATUS_CANNOT_UNSET),
  144.                             "> "        },
  145.     { PS3,            STATUS_EXPORT,        "#? "        },
  146.     { PS4,            STATUS_EXPORT,        "+ "        },
  147.     { HomeVariableName,        STATUS_EXPORT,        null        },
  148.     { ShellVariableName,    STATUS_EXPORT,        null        },
  149.  
  150.     { (char *)NULL,        0 }
  151. };
  152.  
  153.                 /* Entry directory            */
  154. static char    *Start_directory = (char *)NULL;
  155. static time_t    ShellStartTime = 0;    /* Load time of shell        */
  156. static time_t    SecondsOffset = 0;    /* Offset for SECONDS        */
  157.                     /* Original Interrupt 24 address */
  158. #ifndef OS2
  159. static void    (interrupt far *Orig_I24_V) (void);
  160. #endif
  161.  
  162. #ifdef SIGQUIT
  163. static void    (*qflag)(int) = SIG_IGN;
  164. #endif
  165.  
  166. /* Functions */
  167.  
  168. static char * near    CheckClassExpression (char *, int, bool);
  169. static bool near    Initialise (char *);
  170. static void near    ExecuteNextCommand (void);
  171. static void near    CheckForMailArriving (void);
  172. static void near    Pre_Process_Argv (char **, int *);
  173. static void near    LoadGlobalVariableList (void);
  174. static void near    LoadTheProfileFiles (void);
  175. static void near    ConvertUnixPathToMSDOS (void);
  176. static void near    ClearUserPrompts (void);
  177. static void near    SecondAndRandomEV (char *, long);
  178. static void near    SetUpParameterEV (int, char **, char *);
  179. static bool near    AllowedToSetVariable (VariableList *);
  180. static void near    SetUpANumericValue (VariableList *, long, int);
  181. static void near    ProcessErrorExit (void);
  182. static char * near    SuppressSpacesZeros (VariableList *, char *);
  183. static void        AddToNewEnvironment (void *, VISIT, int);
  184. static void        AddToOldEnvironment (void *, VISIT, int);
  185. static void        DeleteEnvironment (void *, VISIT, int);
  186. static int        SearchVariable (void *, void *);
  187. static void near    CheckOPTIND (char *, long);
  188. static void near    CreateIntegerVariables (void);
  189. #ifdef OS2
  190. static void near    CheckForTerminatedProcess (void);
  191. #endif
  192.  
  193. #ifdef OS2
  194. extern ushort        _aenvseg;    /* Environment seg        */
  195. extern ushort        _acmdln;    /* Command line offset        */
  196. #endif
  197.  
  198. /*
  199.  * The main program starts here
  200.  */
  201.  
  202. void main (int argc, register char **argv)
  203. {
  204.     int            cflag = 0;
  205.     int            sc;
  206.     char        *name = *argv;
  207.     int            (*iof)(IO_State *) = File_GetNextCharacter;
  208.                     /* Load up various parts of the    */
  209.                     /* system            */
  210.     bool        OptionsRflag = Initialise (*argv);
  211.     bool        OptionsXflag = FALSE;    /* -x option from    */
  212.                         /* command line        */
  213.     bool        level0 = FALSE;    /* Level zero (read profile)    */
  214.     jmp_buf        ReturnPoint;
  215.  
  216. /*
  217.  * This is to fix a funny in the Microsoft C 6 OS/2 compiler which
  218.  * mis-allocates something in the object file for sh6.c
  219.  */
  220.     e.iop = iostack - 1;
  221.  
  222. #ifdef OS2
  223.     SetWindowName ();
  224. #endif
  225.  
  226. /* Set up start time */
  227.  
  228.     ShellStartTime = time ((time_t *)NULL);
  229.  
  230. /* Preprocess options to convert two character options of the form /x to
  231.  * -x.  Some programs!!
  232.  */
  233.  
  234.     if (argc > 1)
  235.     Pre_Process_Argv (argv, &argc);
  236.  
  237. /* Save the start directory for when we exit */
  238.  
  239.     Start_directory = getcwd ((char *)NULL, PATH_MAX + 4);
  240.  
  241. /* Process the options */
  242.  
  243.     while ((sc = GetOptions (argc, argv,
  244.                  "PRabc:defghijklmnopqrtsuvwxyz0", 0)) != EOF)
  245.     {
  246.     switch (sc)
  247.     {
  248.         case '0':                /* Level 0 flag for DOS    */
  249.         level0 = TRUE;
  250.         break;
  251.  
  252.         case 'r':                /* Restricted        */
  253.         OptionsRflag = TRUE;
  254.         break;
  255.  
  256.         case 'c':                /* Command on line    */
  257.         ClearUserPrompts ();
  258.         cflag = 1;
  259.  
  260.         PUSHIO (aword, OptionArgument,
  261.             iof = Line_GetNextCharacter, null);
  262.         break;
  263.  
  264.         case 'q':                /* No quit ints        */
  265. #ifdef SIGQUIT
  266.         qflag = SIG_DFL;
  267. #endif
  268.         break;
  269.  
  270.  
  271.         case 't':                /* One command        */
  272.         ClearVariableStatus (PS1, STATUS_EXPORT);
  273.         SetVariableFromString (PS1, null);
  274.         iof = FileLine_GetNextCharacter;
  275.         break;
  276.  
  277.         case 'x':
  278.         OptionsXflag = TRUE;
  279.         break;
  280.  
  281. #ifndef OS2
  282.         case 'R':
  283.         Orig_I24_V = (void (far *)())NULL;
  284.         ChangeInitLoad = TRUE;    /* Change load .ini pt.        */
  285.         break;
  286. #else
  287.         case 'P':                /* Use real pipes    */
  288.             GlobalFlags |= FLAGS_REALPIPES;
  289.             break;
  290. #endif
  291.  
  292.         case 'i':                /* Set interactive    */
  293.         InteractiveFlag = TRUE;
  294.  
  295.         case 's':                /* standard input    */
  296.         default:
  297.         if (islower (sc))
  298.             FL_SET (sc);
  299.     }
  300.     }
  301.  
  302.     argv += OptionIndex;
  303.     argc -= OptionIndex;
  304.  
  305. /* Execute one off command - disable prompts */
  306.  
  307.     if ((iof == File_GetNextCharacter) && (argc > 0))
  308.     {
  309.  
  310. /* Open the file if necessary */
  311.  
  312.     if (strcmp ((name = *argv), ShellOptionsVariable))
  313.     {
  314.         ClearUserPrompts ();
  315.  
  316.         if (!AddToStackForExection (name))
  317.         {
  318.         PrintErrorMessage (LIT_Emsg, "cannot open script", name,
  319.                    strerror (errno));
  320.         exit (1);
  321.         }
  322.     }
  323.     }
  324.  
  325. /* Load terminal I/O structure if necessary and load the history file */
  326.  
  327.     if (IOStackPosition (0) & IOSTACK_OUTSIDE)
  328.     {
  329.     PUSHIO (afile, 0, iof, null);
  330.  
  331.     if (isatty (0) && isatty (1) && !cflag)
  332.     {
  333.         FL_SET ('s');
  334.         PrintVersionNumber (stderr);
  335.  
  336.         InteractiveFlag = TRUE;
  337. #ifndef NO_HISTORY
  338.         HistoryEnabled = TRUE;
  339.         LoadHistory ();
  340.         Configure_Keys ();
  341. #endif
  342.     }
  343.     }
  344.  
  345. /* Set up the $- variable */
  346.  
  347.     SetShellSwitches ();
  348.  
  349. #ifdef SIGQUIT
  350.     signal (SIGQUIT, qflag);
  351. #endif
  352.  
  353. /* Read profile ? */
  354.  
  355.     if (((name != (char *)NULL) && (*name == '-')) || level0)
  356.     LoadTheProfileFiles ();
  357.  
  358. /* Set up signals */
  359.  
  360.     if (InteractiveFlag)
  361.     signal (SIGTERM, TerminateSignalled);
  362.  
  363. /* Return point */
  364.  
  365.     if (setjmp (FailReturnPoint = ReturnPoint))
  366.     ExitTheShell (FALSE);
  367.  
  368.     signal (SIGINT, InterruptSignalled);
  369.  
  370. /* Load any parameters */
  371.  
  372.     SetUpParameterEV (argc, argv, name);
  373.  
  374. /* If the xflag was set on the command line, Mark the lowest level IO
  375.  * structure to restore it.
  376.  */
  377.  
  378.     if (OptionsXflag)
  379.     iostack[0].TaskType |= XRESET_XF;
  380.  
  381.     if (OptionsRflag)
  382.     iostack[0].TaskType |= XRESET_RF;
  383.  
  384. /* If we are at IO stack zero, output the prompt */
  385. /* Execute the command loop */
  386.  
  387.     while (1)
  388.     {
  389.  
  390. /* Restart point for interrupts */
  391.  
  392.     setjmp (FailReturnPoint = ReturnPoint);
  393.  
  394. /* If this is the first time, check to see if we need to read the ENV file */
  395.  
  396.     if ((IOStackPosition (0) & IOSTACK_FIRST) && FirstReadFromUser)
  397.     {
  398.         FirstReadFromUser = FALSE;
  399.         AddToStackForExection (GetVariableAsString (ENVVariable, FALSE));
  400.     }
  401.  
  402. /* If we are at IO stack zero, output the prompt */
  403.  
  404.     if (InteractiveFlag && (IOStackPosition (0) & IOSTACK_FIRST))
  405.     {
  406.  
  407. /* Set up a few things for console input - cursor, mail prompt etc */
  408.  
  409.         PositionCursorInColumnZero ();
  410.         CheckForMailArriving ();
  411. #ifdef OS2
  412.         CheckForTerminatedProcess ();
  413. #endif
  414.         LastUserPrompt = PS1;
  415.         CloseAllHandlers ();    /* Clean up any open shell files */
  416.     }
  417.  
  418. /* Read the next command and process it */
  419.  
  420.     ExecuteNextCommand ();
  421.     MemoryAreaLevel = 0;
  422.     }
  423. }
  424.  
  425. /*
  426.  * Set up the value of $-
  427.  */
  428.  
  429. void SetShellSwitches (void)
  430. {
  431.     register char    *cp, c;
  432.     char        m['z' - 'a' + 1];
  433.  
  434.     for (cp = m, c = 'a'; c <= 'z'; ++c)
  435.     {
  436.     if (FL_TEST (c))
  437.         *(cp++) = c;
  438.     }
  439.  
  440.     *cp = 0;
  441.     SetVariableFromString (ShellOptionsVariable, m);
  442. }
  443.  
  444. /* Execute a command */
  445.  
  446. static void near ExecuteNextCommand (void)
  447. {
  448.     register int    i;
  449.     jmp_buf        ReturnPoint;
  450.     C_Op        *outtree = (C_Op *)NULL;
  451.  
  452. /* Exit any previous environments */
  453.  
  454.     while (e.oenv)
  455.     QuitCurrentEnvironment ();
  456.  
  457. /* initialise space */
  458.  
  459.     MemoryAreaLevel = 1;
  460.     FreeAllHereFiles (MemoryAreaLevel);
  461.     ReleaseMemoryArea (MemoryAreaLevel);
  462.     WordListBlock = (Word_B *)NULL;
  463.     IOActionBlock = (Word_B *)NULL;
  464.     e.ErrorReturnPoint = (int *)NULL;
  465.     e.cline = GetAllocatedSpace (LINE_MAX);
  466.     e.eline = e.cline + LINE_MAX - 5;
  467.     e.linep = e.cline;
  468.     AllowMultipleLines = 0;
  469.     InParser = TRUE;
  470.     SW_intr = 0;
  471.     ProcessingEXECCommand = FALSE;
  472.     flushall ();            /* Clear output */
  473.  
  474. /* Get the line and process it */
  475.  
  476.     if (setjmp (FailReturnPoint = ReturnPoint) ||
  477.     ((outtree = BuildParseTree ()) == (C_Op *)NULL) || SW_intr)
  478.     {
  479.  
  480. /* If interrupt occured, remove current Input stream */
  481.  
  482.     if (SW_intr && (IOBasePosition () == IOSTACK_INSIDE))
  483.         CloseUpIOStack (e.iop--, TRUE);
  484.  
  485. /* Quit all environments */
  486.  
  487.     while (e.oenv)
  488.         QuitCurrentEnvironment ();
  489.  
  490.     ScrapHereList ();
  491.  
  492.     if (!InteractiveFlag && SW_intr)
  493.         ExitTheShell (FALSE);
  494.  
  495. /* Exit */
  496.  
  497.     InParser = FALSE;
  498.     SW_intr = 0;
  499.     return;
  500.     }
  501.  
  502. /* Ok - reset some variables and then execute the command tree */
  503.  
  504.     InParser = FALSE;
  505.     Break_List = (Break_C *)NULL;
  506.     Return_List = (Break_C *)NULL;
  507.     SShell_List = (Break_C *)NULL;
  508.     SW_intr = 0;
  509.     ProcessingEXECCommand = FALSE;
  510.     FlushHistoryBuffer ();        /* Save history            */
  511.  
  512. /* Set execute function recursive level and the SubShell count to zero */
  513.  
  514.     Execute_stack_depth = 0;
  515.  
  516. /* Set up Redirection IO (Saved) array and SubShell Environment information */
  517.  
  518.     NSave_IO_E = 0;        /* Number of entries        */
  519.     MSave_IO_E = 0;        /* Max Number of entries    */
  520.     NSubShells = 0;        /* Number of entries        */
  521.     MSubShells = 0;        /* Max Number of entries    */
  522.     CurrentFunction = (FunctionList *)NULL;
  523.     ProcessingDEBUGTrap = FALSE;
  524.     ProcessingERRORTrap = FALSE;
  525.  
  526. /* Ok - if we wail, we need to clean up the stacks */
  527.  
  528.     if ((setjmp (FailReturnPoint = ReturnPoint) == 0) && !FL_TEST ('n'))
  529.     ExecuteParseTree (outtree, NOPIPE, NOPIPE, 0);
  530.  
  531. /* Make sure the I/O and environment are back at level 0 and then clear them */
  532.  
  533.     Execute_stack_depth = 0;
  534.     ClearExtendedLineFile ();
  535.  
  536.     if (NSubShells != 0)
  537.     DeleteGlobalVariableList ();
  538.  
  539.     if (NSave_IO_E)
  540.     RestoreStandardIO (0, TRUE);
  541.  
  542.     if (MSubShells)
  543.     ReleaseMemoryCell ((void *)SubShells);
  544.  
  545.     if (MSave_IO_E)
  546.     ReleaseMemoryCell ((void *)SSave_IO);
  547.  
  548. /* Check for interrupts */
  549.  
  550.     if (!InteractiveFlag && SW_intr)
  551.     {
  552.     ProcessingEXECCommand = FALSE;
  553.     ExitTheShell (FALSE);
  554.     }
  555.  
  556. /* Run any traps that are required */
  557.  
  558.     if ((i = InterruptTrapPending) != 0)
  559.     {
  560.     InterruptTrapPending = 0;
  561.     RunTrapCommand (i);
  562.     }
  563. }
  564.  
  565. /*
  566.  * Terminate current environment with an error
  567.  */
  568.  
  569. void TerminateCurrentEnvironment (void)
  570. {
  571.     flushall ();
  572.     longjmp (FailReturnPoint, 1);
  573.  
  574.     /* NOTREACHED */
  575. }
  576.  
  577. /*
  578.  * Exit the shell
  579.  */
  580.  
  581. void ExitTheShell (bool ReturnRequired)
  582. {
  583.     flushall ();
  584.  
  585.     if (ProcessingEXECCommand)
  586.     TerminateCurrentEnvironment ();
  587.  
  588. #ifndef OS2
  589.     if (Orig_I24_V == (void (far *)())NULL)
  590.     {
  591.     fputs (NOExit, stderr);
  592.  
  593.     if (!ReturnRequired)
  594.         TerminateCurrentEnvironment ();
  595.     }
  596. #endif
  597.  
  598. /* Clean up */
  599.  
  600.     ScrapHereList ();
  601.     FreeAllHereFiles (1);
  602.  
  603. /* Trap zero on exit */
  604.  
  605.     RunTrapCommand (0);
  606.  
  607. /* Dump history on exit */
  608.  
  609. #ifndef NO_HISTORY
  610.     if (InteractiveFlag && isatty(0))
  611.     DumpHistory ();
  612. #endif
  613.  
  614.     CloseAllHandlers ();
  615.  
  616. /* Clear swap file if necessary */
  617.  
  618. #ifndef OS2
  619.     ClearSwapFile ();
  620. #endif
  621.  
  622. /* If this is a command only - restore the directory because DOS doesn't
  623.  * and the user might expect it
  624.  */
  625.  
  626.     if (Start_directory != (char *)NULL)
  627.     RestoreCurrentDirectory (Start_directory);
  628.  
  629. /* If this happens during startup - we restart */
  630.  
  631. #ifndef OS2
  632.     if (Orig_I24_V == (void (far *)())NULL)
  633.     return;
  634. #endif
  635.  
  636. /* Exit - hurray */
  637.  
  638.     exit (ExitStatus);
  639.  
  640. /* NOTREACHED */
  641. }
  642.  
  643. /*
  644.  * Output warning message
  645.  */
  646.  
  647. int PrintWarningMessage (char *fmt, ...)
  648. {
  649.     va_list    ap;
  650.  
  651.     va_start (ap, fmt);
  652.     vfprintf (stderr, fmt, ap);
  653.     ExitStatus = -1;
  654.  
  655. /* If leave on error - exit */
  656.  
  657.     if (FL_TEST ('e'))
  658.     ExitTheShell (FALSE);
  659.  
  660.     va_end (ap);
  661.     return 1;
  662. }
  663.  
  664. /*
  665.  * Shell error message
  666.  */
  667.  
  668. void ShellErrorMessage (char *fmt, ...)
  669. {
  670.     va_list    ap;
  671.  
  672. /* Error message processing */
  673.  
  674.     if (e.iop->FileName == null)
  675.     fputs ("sh: ", stderr);
  676.  
  677.     else
  678.     fprintf (stderr, "%s: at line %d, ", e.iop->FileName, e.iop->LineCount);
  679.  
  680.     va_start (ap, fmt);
  681.     vfprintf (stderr, fmt, ap);
  682.     fputc (CHAR_NEW_LINE, stderr);
  683.     ProcessErrorExit ();
  684.     va_end (ap);
  685. }
  686.  
  687. /*
  688.  * Output error message
  689.  */
  690.  
  691. void PrintErrorMessage (char *fmt, ...)
  692. {
  693.     va_list    ap;
  694.  
  695. /* Error message processing */
  696.  
  697.     va_start (ap, fmt);
  698.     vfprintf (stderr, fmt, ap);
  699.     ProcessErrorExit ();
  700.     va_end (ap);
  701. }
  702.  
  703. /*
  704.  * Common processing for PrintErrorMessage and ShellError Message
  705.  */
  706.  
  707. static void near ProcessErrorExit (void)
  708. {
  709.     ExitStatus = -1;
  710.  
  711.     if (FL_TEST ('e'))
  712.     ExitTheShell (FALSE);
  713.  
  714. /* Error processing */
  715.  
  716.     if (FL_TEST ('n'))
  717.     return;
  718.  
  719. /* If not interactive - exit */
  720.  
  721.     if (!InteractiveFlag)
  722.     ExitTheShell (FALSE);
  723.  
  724.     if (e.ErrorReturnPoint)
  725.     longjmp (e.ErrorReturnPoint, 1);
  726.  
  727. /* CloseAllHandlers (); Removed - caused problems.  There may be problems
  728.  * remaining with files left open?
  729.  */
  730.  
  731.     while (IOStackPosition (0) & IOSTACK_INSIDE)
  732.     CloseUpIOStack (e.iop--, TRUE);
  733.  
  734.     e.iop = e.iobase = iostack;
  735. }
  736.  
  737. /*
  738.  * Create or delete a new environment.  If f is set, delete the environment
  739.  */
  740.  
  741. bool CreateNewEnvironment (int ErrorCode)
  742. {
  743.     register ShellFileEnvironment    *ep;
  744.  
  745. /* Delete environment? */
  746.  
  747.     if (ErrorCode)
  748.     {
  749.     QuitCurrentEnvironment ();
  750.     return TRUE;
  751.     }
  752.  
  753. /* Create a new environment */
  754.  
  755.     if ((ep = (ShellFileEnvironment *)
  756.         GetAllocatedSpace (sizeof (ShellFileEnvironment)))
  757.         == (ShellFileEnvironment *)NULL)
  758.     {
  759.     while (e.oenv)
  760.         QuitCurrentEnvironment ();
  761.  
  762.     TerminateCurrentEnvironment ();
  763.     }
  764.  
  765.     *ep = e;
  766.     e.eof_p = FALSE;            /* Disable EOF processing    */
  767.     e.oenv  = ep;
  768.     e.ErrorReturnPoint = ErrorReturnPoint;
  769.     return FALSE;
  770. }
  771.  
  772. /*
  773.  * Exit the current environment successfully
  774.  */
  775.  
  776. void QuitCurrentEnvironment (void)
  777. {
  778.     register ShellFileEnvironment    *ep;
  779.     register int            fd;
  780.  
  781. /* Restore old environment, delete the space and close any files opened in
  782.  * this environment
  783.  */
  784.  
  785.     if ((ep = e.oenv) != (ShellFileEnvironment *)NULL)
  786.     {
  787.  
  788. /* Get the files used in this environment to close */
  789.  
  790.     fd = e.FirstAvailableFileHandler;
  791.     e = *ep;
  792.  
  793.     ReleaseMemoryCell ((void *)ep);
  794.  
  795.     while (--fd >= e.FirstAvailableFileHandler)
  796.         S_close (fd, TRUE);
  797.     }
  798. }
  799.  
  800. /*
  801.  * Is character c in s?
  802.  */
  803.  
  804. bool any (register char c, register char *s)
  805. {
  806.     while (*s)
  807.     {
  808.     if (*(s++) == c)
  809.         return TRUE;
  810.     }
  811.  
  812.     return FALSE;
  813. }
  814.  
  815. /*
  816.  * Convert binary to ascii
  817.  */
  818.  
  819. char *IntegerToString (register int n)
  820. {
  821.     static char        nt[10];
  822.  
  823.     sprintf (nt, "%u", n);
  824.     return nt;
  825. }
  826.  
  827. /*
  828.  * SIGINT interrupt processing
  829.  */
  830.  
  831. void InterruptSignalled (int signo)
  832. {
  833. /* Restore signal processing and set SIGINT detected flag */
  834.  
  835.     signal (signo, InterruptSignalled);
  836.     SW_intr = 1;
  837.  
  838. /* Zap the swap file, just in case it got corrupted */
  839.  
  840. #ifndef OS2
  841.     S_close (SW_fp, TRUE);
  842.     ClearSwapFile ();
  843. #endif
  844.  
  845. /* Are we talking to the user?  Yes - check in parser */
  846.  
  847.     if (InteractiveFlag)
  848.     {
  849.     if (InParser)
  850.         fputc (CHAR_NEW_LINE, stderr);
  851.  
  852. /* Abandon processing */
  853.  
  854.     TerminateCurrentEnvironment ();
  855.     }
  856.  
  857. /* No - exit */
  858.  
  859.     else
  860.     {
  861.     ProcessingEXECCommand = FALSE;
  862.     ExitStatus = 1;
  863.     ExitTheShell (FALSE);
  864.     }
  865. }
  866.  
  867. /*
  868.  * Grap some space and check for an error
  869.  */
  870.  
  871. char *GetAllocatedSpace (size_t n)
  872. {
  873.     register char *cp;
  874.  
  875.     if ((cp = AllocateMemoryCell (n)) == (char *)NULL)
  876.     PrintErrorMessage (BasicErrorMessage, ShellNameLiteral, Outofmemory1);
  877.  
  878.     return cp;
  879. }
  880.  
  881. /*
  882.  * Save a string in a given area
  883.  */
  884.  
  885. char *StringSave (register char *s)
  886. {
  887.     register char    *cp;
  888.  
  889.     if ((cp = GetAllocatedSpace (strlen (s) + 1)) != (char *)NULL)
  890.     {
  891.     SetMemoryAreaNumber ((void *)cp, 0);
  892.     return strcpy (cp, s);
  893.     }
  894.  
  895.     return null;
  896. }
  897.  
  898. /*
  899.  * Duplicate at current Memory level
  900.  */
  901.  
  902. char *StringCopy (register char *s)
  903. {
  904.     register char    *cp;
  905.  
  906.     if ((cp = GetAllocatedSpace (strlen (s) + 1)) != (char *)NULL)
  907.     return strcpy (cp, s);
  908.  
  909.     return null;
  910. }
  911.  
  912. /*
  913.  * trap handling - Save signal number and restore signal processing
  914.  */
  915.  
  916. void TerminateSignalled (register int i)
  917. {
  918.     if (i == SIGINT)        /* Need this because swapper sets it    */
  919.     {
  920.     SW_intr = 0;
  921.  
  922. /* Zap the swap file, just in case it got corrupted */
  923.  
  924. #ifndef OS2
  925.     S_close (SW_fp, TRUE);
  926.     ClearSwapFile ();
  927. #endif
  928.     }
  929.  
  930.     InterruptTrapPending = i;
  931.     signal (i, TerminateSignalled);
  932. }
  933.  
  934. /*
  935.  * Execute a trap command
  936.  *
  937.  *  0 - exit trap
  938.  * -1 - Debug Trap
  939.  * -2 - Error Trap
  940.  */
  941.  
  942. void RunTrapCommand (int i)
  943. {
  944.     char    *trapstr;
  945.     char    tval[10];
  946.     char    *tvalp = tval;
  947.  
  948. /* Check for special values and recursion */
  949.  
  950.     if (i == -1)
  951.     {
  952.     tvalp = Trap_DEBUG;
  953.  
  954.     if (ProcessingDEBUGTrap)
  955.         return;
  956.  
  957.     ProcessingDEBUGTrap = TRUE;
  958.     }
  959.  
  960. /* Error trap */
  961.  
  962.     else if (i == -2)
  963.     {
  964.     tvalp = Trap_ERR;
  965.  
  966.     if (ProcessingERRORTrap)
  967.         return;
  968.  
  969.     ProcessingERRORTrap = TRUE;
  970.     }
  971.  
  972.     else
  973.     {
  974.     *tval = '~';
  975.     itoa (i, tval + 1, 10);
  976.     }
  977.  
  978.     if ((trapstr = GetVariableAsString (tvalp, FALSE)) == null)
  979.     return;
  980.  
  981. /* If signal zero, save a copy of the trap value and then delete the trap */
  982.  
  983.     if (i == 0)
  984.     {
  985.     trapstr = StringCopy (trapstr);
  986.     UnSetVariable (tval, TRUE);
  987.     }
  988.  
  989.     RUN (aword, trapstr, Line_GetNextCharacter, TRUE, null, (char **)NULL);
  990.     ProcessingDEBUGTrap = FALSE;
  991.     ProcessingERRORTrap = FALSE;
  992. }
  993.  
  994. /*
  995.  * Find the given name in the dictionary and return its value.  If the name was
  996.  * not previously there, enter it now and return a null value.
  997.  */
  998.  
  999. VariableList    *LookUpVariable (register char *name, bool cflag)
  1000. {
  1001.     register VariableList    *vp;
  1002.     VariableList        **vpp;
  1003.     register int        c;
  1004.     static VariableList        dummy;
  1005.     void            (*save_signal)(int);
  1006.  
  1007. /* Set up the dummy variable */
  1008.  
  1009.     memset (&dummy, 0, sizeof (VariableList));
  1010.     dummy.name = name;
  1011.     dummy.status = STATUS_READONLY;
  1012.     dummy.value = null;
  1013.  
  1014. /* If digit string - use the dummy to return the value */
  1015.  
  1016.     if (isdigit (*name))
  1017.     {
  1018.     for (c = 0; isdigit (*name) && (c < 1000); name++)
  1019.         c = c * 10 + *name - '0';
  1020.  
  1021.     dummy.value = (c <= ParameterCount) ? ParameterArray[c] : null;
  1022.     return &dummy;
  1023.     }
  1024.  
  1025. /* Look up in list */
  1026.  
  1027.     vpp = (VariableList **)tfind (name, &VariableTree, FindVariable);
  1028.  
  1029. /* If we found it, return it */
  1030.  
  1031.     if (vpp != (VariableList **)NULL)
  1032.     {
  1033.         vp = *vpp;
  1034.  
  1035. /* Special processing for SECONDS and RANDOM */
  1036.  
  1037.     if (!strcmp (name, SecondsVariable) &&
  1038.         !(DisabledVariables & DISABLE_SECONDS))
  1039.         SetUpANumericValue (vp, time ((time_t *)NULL) - ShellStartTime +
  1040.                     SecondsOffset, 10);
  1041.  
  1042.     else if (!strcmp (name, RandomVariable) &&
  1043.          !(DisabledVariables & DISABLE_RANDOM))
  1044.         SetUpANumericValue (vp, (long)rand(), 10);
  1045.  
  1046.     return vp;
  1047.     }
  1048.  
  1049. /* If we don't want to create it, return a dummy */
  1050.  
  1051.     dummy.status |= STATUS_NOEXISTANT;
  1052.  
  1053.     if (!cflag)
  1054.     return &dummy;
  1055.  
  1056. /* Create a new variable.  If no memory, use the dummy */
  1057.  
  1058.     dummy.name = null;
  1059.  
  1060.     if ((vp = (VariableList *)GetAllocatedSpace (sizeof (VariableList)))
  1061.         == (VariableList *)NULL)
  1062.     return &dummy;
  1063.  
  1064.     if ((vp->name = StringCopy (name)) == null)
  1065.     {
  1066.     ReleaseMemoryCell ((void *)vp);
  1067.     return &dummy;
  1068.     }
  1069.  
  1070. /* Save signals */
  1071.  
  1072.     save_signal = signal (SIGINT, SIG_IGN);
  1073.  
  1074. /* Add to the tree */
  1075.  
  1076.     if (tsearch (vp, &VariableTree, SearchVariable) == (void *)NULL)
  1077.     {
  1078.     ReleaseMemoryCell ((void *)vp->name);
  1079.     ReleaseMemoryCell ((void *)vp);
  1080.         return &dummy;
  1081.     }
  1082.  
  1083. /* OK Added OK - set up memory */
  1084.  
  1085.     SetMemoryAreaNumber ((void *)vp, 0);
  1086.     SetMemoryAreaNumber ((void *)vp->name, 0);
  1087.     vp->value = null;
  1088.  
  1089. /* Restore signals */
  1090.  
  1091.     signal (SIGINT, save_signal);
  1092.  
  1093.     return vp;
  1094. }
  1095.  
  1096. /*
  1097.  * TWALK compare functions for VARIABLE tree
  1098.  *
  1099.  * Note: We know about these two function, so we know the key is always
  1100.  *       the first parameter.  So we only pass the char * not the FunctionList
  1101.  *       pointer.
  1102.  */
  1103.  
  1104. int FindVariable (void *key1, void *key2)
  1105. {
  1106.     return strcmp (key1, ((VariableList *)key2)->name);
  1107. }
  1108.  
  1109. /*
  1110.  * TSEARCH - Search the VARIABLE TREE for an entry
  1111.  */
  1112.  
  1113. static int SearchVariable (void *key1, void *key2)
  1114. {
  1115.     return strcmp (((VariableList *)key1)->name, ((VariableList *)key2)->name);
  1116. }
  1117.  
  1118. /*
  1119.  * Execute an assignment.  If a valid assignment, load it into the variable
  1120.  * list.
  1121.  */
  1122.  
  1123. bool AssignVariableFromString (register char *s)
  1124. {
  1125.     register char    *cp;
  1126.  
  1127. /* Ignore if not valid environment variable - check alpha and equals */
  1128.  
  1129.     if (IsValidVariableName (s) != '=')
  1130.     return FALSE;
  1131.  
  1132. /* Assign the value */
  1133.  
  1134.     *(cp = strchr (s, '=')) = 0;
  1135.     SetVariableFromString (s, ++cp);
  1136.     SecondAndRandomEV (s, atol (cp));
  1137.     return TRUE;
  1138. }
  1139.  
  1140. /*
  1141.  * Duplicate the Variable List for a Subshell
  1142.  *
  1143.  * Create a new Var_list environment for a Sub Shell
  1144.  */
  1145.  
  1146. int CreateGlobalVariableList (unsigned char Function)
  1147. {
  1148.     int            i;
  1149.     S_SubShell        *sp;
  1150.  
  1151.     for (sp = SubShells, i = 0; (i < NSubShells) &&
  1152.                    (SubShells[i].depth < Execute_stack_depth);
  1153.      i++);
  1154.  
  1155. /* If depth is greater or equal to the Execute_stack_depth - we should panic
  1156.  * as this should not happen.  However, for the moment, I'll ignore it
  1157.  */
  1158.  
  1159.     if (NSubShells == MSubShells)
  1160.     {
  1161.     sp = (S_SubShell *) GetAllocatedSpace ((MSubShells + SSAVE_IO_SIZE) *
  1162.                         sizeof (S_SubShell));
  1163.  
  1164. /* Check for error */
  1165.  
  1166.     if (sp == (S_SubShell *)NULL)
  1167.         return -1;
  1168.  
  1169. /* Save original data */
  1170.  
  1171.     if (MSubShells != 0)
  1172.     {
  1173.         memcpy (sp, SubShells, sizeof (S_SubShell) * MSubShells);
  1174.         ReleaseMemoryCell ((void *)SubShells);
  1175.     }
  1176.  
  1177.     SetMemoryAreaNumber ((void *)sp, 0);
  1178.     SubShells = sp;
  1179.     MSubShells += SSAVE_IO_SIZE;
  1180.     }
  1181.  
  1182. /* Save the depth and the old Variable Tree value */
  1183.  
  1184.     sp = &SubShells[NSubShells++];
  1185.     sp->OldVariableTree = VariableTree;
  1186.     sp->depth  = Execute_stack_depth;
  1187.     sp->GFlags = GlobalFlags | Function;
  1188.     sp->Eflags = flags;
  1189.     VariableTree = (void *)NULL;
  1190.  
  1191. /* Duplicate the old Variable list */
  1192.  
  1193.     ATNE_Function = Function;
  1194.     twalk (sp->OldVariableTree, AddToNewEnvironment);
  1195.  
  1196. /* Reset global values */
  1197.  
  1198.     LoadGlobalVariableList ();
  1199.     return 0;
  1200. }
  1201.  
  1202. /*
  1203.  * TWALK - add to new environment
  1204.  */
  1205.  
  1206. static void AddToNewEnvironment (void *key, VISIT visit, int level)
  1207. {
  1208.     VariableList    *vp = *(VariableList **)key;
  1209.     VariableList    *vp1;
  1210.  
  1211.     if ((visit == postorder) || (visit == leaf))
  1212.     {
  1213.  
  1214. /* For functions, do not copy the traps */
  1215.  
  1216.     if (ATNE_Function && (*vp->name == '~') && vp->name[1])
  1217.         return;
  1218.  
  1219. /* Create a new entry */
  1220.  
  1221.     vp1 = LookUpVariable (vp->name, TRUE);
  1222.  
  1223.     if ((!(vp->status & STATUS_INTEGER)) && (vp->value != null))
  1224.         vp1->value = StringSave (vp->value);
  1225.  
  1226. /* Copy some flags */
  1227.  
  1228.     vp1->status = vp->status;
  1229.     vp1->nvalue = vp->nvalue;
  1230.     vp1->base = vp->base;
  1231.     vp1->width = vp->width;
  1232.     }
  1233. }
  1234.  
  1235. /*
  1236.  * Delete a SubShell environment and restore the original
  1237.  */
  1238.  
  1239. void DeleteGlobalVariableList (void)
  1240. {
  1241.     int            j;
  1242.     S_SubShell        *sp;
  1243.     VariableList    *vp;
  1244.     void        (*save_signal)(int);
  1245.  
  1246.     for (j = NSubShells; j > 0; j--)
  1247.     {
  1248.        sp = &SubShells[j - 1];
  1249.  
  1250.        if (sp->depth < Execute_stack_depth)
  1251.        break;
  1252.  
  1253. /* Reduce number of entries */
  1254.  
  1255.     --NSubShells;
  1256.  
  1257. /* Disable signals */
  1258.  
  1259.     save_signal = signal (SIGINT, SIG_IGN);
  1260.  
  1261. /* Restore the previous level information */
  1262.  
  1263.     vp = VariableTree;
  1264.     VariableTree = sp->OldVariableTree;
  1265.     GlobalFlags = (unsigned char)(sp->GFlags & ~FLAGS_FUNCTION);
  1266.     flags = sp->Eflags;
  1267.  
  1268. /* Release the space */
  1269.  
  1270.     ATOE_GFlags = sp->GFlags;
  1271.  
  1272.     twalk (vp, AddToOldEnvironment);
  1273.     twalk (vp, DeleteEnvironment);
  1274.  
  1275. /* Restore signals */
  1276.  
  1277.     signal (SIGINT, save_signal);
  1278.  
  1279.     LoadGlobalVariableList ();
  1280.     }
  1281. }
  1282.  
  1283. /*
  1284.  * TWALK - delete old environment tree
  1285.  */
  1286.  
  1287. static void DeleteEnvironment (void *key, VISIT visit, int level)
  1288. {
  1289.     VariableList    *vp = *(VariableList **)key;
  1290.  
  1291.     if ((visit == endorder) || (visit == leaf))
  1292.     {
  1293.         if (vp->value == null)
  1294.             ReleaseMemoryCell ((void *)vp->value);
  1295.  
  1296.         ReleaseMemoryCell ((void *)vp->name);
  1297.         ReleaseMemoryCell ((void *)vp);
  1298.     }
  1299. }
  1300.  
  1301. /*
  1302.  * TWALK - Transfer Current Environment to the Old one
  1303.  */
  1304.  
  1305. static void AddToOldEnvironment (void *key, VISIT visit, int level)
  1306. {
  1307.     VariableList    *vp = *(VariableList **)key;
  1308.     VariableList    *vp1;
  1309.  
  1310.     if ((visit == postorder) || (visit == leaf))
  1311.     {
  1312.  
  1313. /* Skip local variables and traps */
  1314.  
  1315.     if ((ATOE_GFlags & FLAGS_FUNCTION) && (!(vp->status & STATUS_LOCAL)) &&
  1316.         (((*vp->name != '~') || !vp->name[1])))
  1317.     {
  1318.  
  1319. /* Get the entry in the old variable list and update it with the new
  1320.  * parameters
  1321.  */
  1322.         vp1 = LookUpVariable (vp->name, TRUE);
  1323.  
  1324.         if (vp1->value != null)
  1325.         ReleaseMemoryCell ((void *)vp1->value);
  1326.  
  1327.         vp1->value = vp->value;
  1328.         vp->value = null;        /* Stop releaseing this as its tx */
  1329.  
  1330.         vp1->status = vp->status;
  1331.         vp1->nvalue = vp->nvalue;
  1332.         vp1->base   = vp->base;
  1333.         vp1->width  = vp->width;
  1334.     }
  1335.     }
  1336. }
  1337.  
  1338. /*
  1339.  * Load GLobal Var List values
  1340.  */
  1341.  
  1342. static void near LoadGlobalVariableList (void)
  1343. {
  1344.     CurrentDirectory = LookUpVariable (tilde, TRUE);
  1345.     RestoreCurrentDirectory (CurrentDirectory->value);
  1346. }
  1347.  
  1348. /*
  1349.  * Match a pattern as in sh(1).  Enhancement to handle prefix processing
  1350.  *
  1351.  * IgnoreCase - ignore case on comparisions.
  1352.  * end - end of match in 'string'.
  1353.  * mode - mode for match processing - see GM_ flags in sh.h
  1354.  */
  1355.  
  1356. bool GeneralPatternMatch (register char *string, register char *pattern,
  1357.               bool IgnoreCase, char **end, int mode)
  1358. {
  1359.     register int    string_c, pattern_c;
  1360.     char        *save_end;
  1361.  
  1362.     if ((string == (char *)NULL) || (pattern == (char *)NULL))
  1363.     return FALSE;
  1364.  
  1365.     while ((pattern_c = *(pattern++)) != '\0')
  1366.     {
  1367.     string_c = *(string++);
  1368.  
  1369.     switch (pattern_c)
  1370.     {
  1371.         case CHAR_OPEN_BRACKETS:    /* Class expression        */
  1372.         if ((pattern =
  1373.             CheckClassExpression (pattern, string_c, IgnoreCase))
  1374.             == (char *)NULL)
  1375.             return FALSE;
  1376.  
  1377.         break;
  1378.  
  1379.         case '?':            /* Match any character        */
  1380.         if (string_c == 0)
  1381.             return FALSE;
  1382.  
  1383.         break;
  1384.  
  1385.         case '*':            /* Match as many as possible    */
  1386.         --string;
  1387.         save_end = (char *)NULL;
  1388.  
  1389.         do
  1390.         {
  1391.             if (!*pattern ||
  1392.             GeneralPatternMatch (string, pattern, IgnoreCase, end,
  1393.                          mode))
  1394.             {
  1395.             if (mode == GM_LONGEST)
  1396.                 save_end = *end;
  1397.  
  1398.             else
  1399.                 return TRUE;
  1400.             }
  1401.  
  1402.         } while (*(string++));
  1403.  
  1404.         if (end != (char **)NULL)
  1405.             *end = save_end;
  1406.  
  1407.         return (save_end == (char *)NULL) ? FALSE : TRUE;
  1408.  
  1409.         case '\\':        /* Escape the next value        */
  1410.         if (*pattern)
  1411.             pattern_c = *(pattern++);
  1412.  
  1413.         default:        /* Match                */
  1414.         if (IgnoreCase)
  1415.         {
  1416.             string_c = tolower (string_c);
  1417.             pattern_c = tolower (pattern_c);
  1418.         }
  1419.  
  1420.         if (string_c != pattern_c)
  1421.             return FALSE;
  1422.     }
  1423.     }
  1424.  
  1425.     if (end != (char **)NULL)
  1426.     {
  1427.     *end = string;
  1428.     return TRUE;
  1429.     }
  1430.  
  1431.     return (*string == 0) ? TRUE : FALSE;
  1432. }
  1433.  
  1434. /*
  1435.  * Process a class expression - []
  1436.  */
  1437.  
  1438. static char * near CheckClassExpression (register char *pattern,
  1439.                      register int string_c, bool IgnoreCase)
  1440. {
  1441.     register int    llimit_c, ulimit_c, not, found;
  1442.  
  1443. /* Exclusive or inclusive class */
  1444.  
  1445.     if ((not = *pattern == CHAR_NOT) != 0)
  1446.     pattern++;
  1447.  
  1448.     found = not;
  1449.  
  1450.     do
  1451.     {
  1452.     if (!*pattern)
  1453.         return (char *)NULL;
  1454.  
  1455. /* Get the next character in class, converting to lower case if necessary */
  1456.  
  1457.     llimit_c = IgnoreCase ? tolower (*pattern) : *pattern;
  1458.  
  1459. /* If this is a range, get the end of range character */
  1460.  
  1461.     if ((*(pattern + 1) == '-') && (*(pattern + 2) != CHAR_CLOSE_BRACKETS))
  1462.     {
  1463.         ulimit_c = IgnoreCase ? tolower (*(pattern + 2)) : *(pattern + 2);
  1464.         pattern++;
  1465.     }
  1466.  
  1467.     else
  1468.         ulimit_c = llimit_c;
  1469.  
  1470. /* Is the current character in the class? */
  1471.  
  1472.     if ((llimit_c <= string_c) && (string_c <= ulimit_c))
  1473.         found = !not;
  1474.  
  1475.     } while (*(++pattern) != CHAR_CLOSE_BRACKETS);
  1476.  
  1477.     return found ? pattern + 1 : (char *)NULL;
  1478. }
  1479.  
  1480. /*
  1481.  * Suffix processing - find the longest/shortest suffix.
  1482.  */
  1483.  
  1484. bool SuffixPatternMatch (register char *string, register char *pattern,
  1485.              char **start, int mode)
  1486. {
  1487.     char    *save_start = (char *)NULL;
  1488.  
  1489. /* Scan the string, looking for a match to the end */
  1490.  
  1491.     while (*string)
  1492.     {
  1493.     if (GeneralPatternMatch (string, pattern, FALSE, (char **)NULL, GM_ALL))
  1494.     {
  1495.  
  1496. /* If longest, stop here */
  1497.  
  1498.         if (mode == GM_LONGEST)
  1499.         {
  1500.         *start = string;
  1501.         return TRUE;
  1502.         }
  1503.  
  1504. /* Save the start of the shortest string so far and continue */
  1505.  
  1506.         save_start = string;
  1507.     }
  1508.  
  1509.     ++string;
  1510.     }
  1511.  
  1512.     return ((*start = save_start) == (char *)NULL) ? FALSE : TRUE;
  1513. }
  1514.  
  1515. /*
  1516.  * Get a string in a malloced area
  1517.  */
  1518.  
  1519. char *AllocateMemoryCell (size_t nbytes)
  1520. {
  1521.     s_region        *np;
  1522.     void        (*save_signal)(int);
  1523. #ifdef OS2_DOSALLOC
  1524.     SEL            sel;
  1525. #endif
  1526.  
  1527.     if (nbytes == 0)
  1528.     abort ();    /* silly and defeats the algorithm */
  1529.  
  1530. /* Grab some space */
  1531.  
  1532. #ifdef OS2_DOSALLOC
  1533.     if (DosAllocSeg (nbytes + sizeof (s_region), &sel, SEG_NONSHARED))
  1534.     {
  1535.     errno = ENOMEM;
  1536.     return (char *)NULL;
  1537.     }
  1538.  
  1539.     np = (s_region *)MAKEP (sel, 0);
  1540.     memset (np, 0, nbytes + sizeof (s_region));
  1541.  
  1542. #else
  1543.     if ((np = (s_region *)calloc (nbytes + sizeof (s_region), 1))
  1544.         == (s_region *)NULL)
  1545.     {
  1546.     errno = ENOMEM;
  1547.         return (char *)NULL;
  1548.     }
  1549. #endif
  1550.  
  1551. /* Disable signals */
  1552.  
  1553.     save_signal = signal (SIGINT, SIG_IGN);
  1554.  
  1555. /* Link into chain */
  1556.  
  1557.     np->next = MemoryAreaHeader;
  1558.     np->area = MemoryAreaLevel;
  1559. #ifdef DEBUG_MEMORY
  1560.     np->nbytes = nbytes;
  1561. #endif
  1562.     MemoryAreaHeader = np;
  1563.  
  1564. /* Restore signals */
  1565.  
  1566.     signal (SIGINT, save_signal);
  1567.  
  1568.     return ((char *)np) + sizeof (s_region);
  1569. }
  1570.  
  1571. /*
  1572.  * Free a string in a malloced area
  1573.  */
  1574.  
  1575. void ReleaseMemoryCell (void *s)
  1576. {
  1577.     s_region        *cp = MemoryAreaHeader;
  1578.     s_region        *lp = (s_region *)NULL;
  1579.     s_region        *sp = (s_region *)((char *)s - sizeof (s_region));
  1580.     void        (*save_signal)(int);
  1581.  
  1582. /* Disable signals */
  1583.  
  1584.     save_signal = signal (SIGINT, SIG_IGN);
  1585.  
  1586. /* Find the string in the chain */
  1587.  
  1588.     if (s != (char *)NULL)
  1589.     {
  1590.     while (cp != (s_region *)NULL)
  1591.     {
  1592.         if (cp != sp)
  1593.         {
  1594.         lp = cp;
  1595.         cp = cp->next;
  1596.         continue;
  1597.         }
  1598.  
  1599. /* First in chain ? */
  1600.  
  1601.         else if (lp == (s_region *)NULL)
  1602.         MemoryAreaHeader = cp->next;
  1603.  
  1604. /* Delete the current entry and relink */
  1605.  
  1606.         else
  1607.         lp->next = cp->next;
  1608.  
  1609.         RELEASE_MEMORY (cp);
  1610.         break;
  1611.     }
  1612.     }
  1613.  
  1614. /* Restore signals */
  1615.  
  1616.     signal (SIGINT, save_signal);
  1617. }
  1618.  
  1619. /*
  1620.  * Check for memory leaks with a dump
  1621.  */
  1622.  
  1623. #ifdef DEBUG_MEMORY
  1624. void DumpMemoryCells (int status)
  1625. {
  1626.     s_region        *cp = MemoryAreaHeader;
  1627.     size_t        i;
  1628.     char        buffer[17];
  1629.     char        *sp;
  1630.  
  1631. /* Find the string in the chain */
  1632.  
  1633.     while (cp != (s_region *)NULL)
  1634.     {
  1635.     fprintf (stderr, "Segment 0x%.8lx Area %5d Length %5d Link 0x%.8lx\n",
  1636.          cp, cp->area, cp->nbytes, cp->next);
  1637.     
  1638.     memset (buffer, ' ', 17);
  1639.     buffer[16] = 0;
  1640.  
  1641.     sp = ((char *)cp) + sizeof (s_region);
  1642.  
  1643.     for (i = 0; i < (((cp->nbytes - 1)/16) + 1) * 16; i++)
  1644.     {
  1645.         if (i >= cp->nbytes)
  1646.         {
  1647.         fputs ("   ", stderr);
  1648.         buffer [i % 16] = ' ';
  1649.         }
  1650.         
  1651.         else
  1652.         {
  1653.         fprintf (stderr, "%.2x ", *sp & 0x0ff);
  1654.         buffer [i % 16] = (char)(isprint (*sp) ? *sp : '.');
  1655.         }
  1656.  
  1657.         if (i % 16 == 15)
  1658.         fprintf (stderr, "    [%s]\n", buffer);
  1659.         
  1660.         sp++;
  1661.     }
  1662.  
  1663.     fputc ('\n', stderr);
  1664.     cp = cp->next;
  1665.     }
  1666. #undef exit
  1667.     exit (status);
  1668. #define exit(x)        DumpMemoryCells (x)
  1669. }
  1670. #endif
  1671.  
  1672. /*
  1673.  * Autodelete space nolonger required.  Ie. Free all the strings in a malloced
  1674.  * area
  1675.  */
  1676.  
  1677. void ReleaseMemoryArea (register int a)
  1678. {
  1679.     s_region        *cp = MemoryAreaHeader;
  1680.     s_region        *lp = (s_region *)NULL;
  1681.     void        (*save_signal)(int);
  1682.  
  1683. /* Disable signals */
  1684.  
  1685.     save_signal = signal (SIGINT, SIG_IGN);
  1686.  
  1687.     while (cp != (s_region *)NULL)
  1688.     {
  1689.  
  1690. /* Is the area number less than that specified - yes, continue */
  1691.  
  1692.     if (cp->area < a)
  1693.     {
  1694.         lp = cp;
  1695.         cp = cp->next;
  1696.     }
  1697.  
  1698. /* OK - delete the area.  Is it the first in chain ?  Yes, delete, relink
  1699.  * and update start location
  1700.  */
  1701.  
  1702.     else if (lp == (s_region *)NULL)
  1703.     {
  1704.         lp = cp;
  1705.         cp = cp->next;
  1706.         MemoryAreaHeader = cp;
  1707.  
  1708.         RELEASE_MEMORY (lp);
  1709.         lp = (s_region *)NULL;
  1710.     }
  1711.  
  1712. /* Not first, delete the current entry and relink */
  1713.  
  1714.     else
  1715.     {
  1716.         lp->next = cp->next;
  1717.         RELEASE_MEMORY (cp);
  1718.         cp = lp->next;
  1719.     }
  1720.     }
  1721.  
  1722. /* Restore signals */
  1723.  
  1724.     signal (SIGINT, save_signal);
  1725. }
  1726.  
  1727. /*
  1728.  * Set the area number for a malloced string.  This allows autodeletion of
  1729.  * space that is nolonger required.
  1730.  */
  1731.  
  1732. void SetMemoryAreaNumber (void *cp, int a)
  1733. {
  1734.     s_region    *sp = (s_region *)((char *)cp - sizeof (s_region));
  1735.  
  1736.     if (cp != (void *)NULL)
  1737.     sp->area = a;
  1738. }
  1739.  
  1740. /*
  1741.  * Get the area number for a malloced string
  1742.  */
  1743.  
  1744. int GetMemoryAreaNumber (void *cp)
  1745. {
  1746.     s_region    *sp = (s_region *)((char *)cp - sizeof (s_region));
  1747.  
  1748.     return sp->area;
  1749. }
  1750.  
  1751. /* Output one of the Prompt.  We save the prompt for the history part of
  1752.  * the program
  1753.  */
  1754.  
  1755. void OutputUserPrompt (char *s)
  1756. {
  1757.     struct tm        *tm;
  1758.     time_t        xtime = time ((time_t *)NULL);
  1759.     int            i;
  1760.     char        buf[PATH_MAX + 4];
  1761.  
  1762.     if (LastUserPrompt != (char *)NULL)
  1763.     {
  1764.     LastUserPrompt = s;        /* Save the Last prompt id    */
  1765.     s = GetVariableAsString (s, TRUE); /* Get the string value    */
  1766.     }
  1767.  
  1768.     else
  1769.     s = LastUserPrompt1;
  1770.  
  1771.     tm = localtime (&xtime);
  1772.  
  1773.     while (*s)
  1774.     {
  1775.  
  1776. /* If a format character, process it */
  1777.  
  1778.     if (*s == '%')
  1779.     {
  1780.         s++;
  1781.         *s = (char)tolower(*s);
  1782.  
  1783.         if (*s == '%')
  1784.         putchar ('%');
  1785.  
  1786.         else
  1787.         {
  1788.         *buf = 0;
  1789.  
  1790.         switch (*(s++))
  1791.         {
  1792.             case 'e':            /* Current event number */
  1793.             if (HistoryEnabled)
  1794.                 itoa (Current_Event + 1, buf, 10);
  1795.  
  1796.             break;
  1797.  
  1798.             case 't':            /* time        */
  1799.             sprintf (buf,"%.2d:%.2d", tm->tm_hour, tm->tm_min);
  1800.             break;
  1801.  
  1802.             case 'd':            /* date        */
  1803.             sprintf (buf, "%.3s %.2d-%.2d-%.2d",
  1804.                  &"SunMonTueWedThuFriSat"[tm->tm_wday * 3],
  1805.                  tm->tm_mday, tm->tm_mon + 1,
  1806.                  tm->tm_year % 100);
  1807.             break;
  1808.  
  1809.             case 'p':            /* directory    */
  1810.             case 'n':            /* default drive */
  1811.             strcpy (buf, CurrentDirectory->value);
  1812.  
  1813.             if (*(s - 1) == 'n')
  1814.                 buf[1] = 0;
  1815.  
  1816.             break;
  1817.  
  1818.             case 'v':            /* version        */
  1819. #ifdef OS2
  1820.             sprintf (buf, "OS/2 %.2d:%.2d", _osmajor / 10,
  1821.                  _osminor);
  1822. #else
  1823.             sprintf (buf, "MS-DOS %.2d:%.2d", _osmajor, _osminor);
  1824. #endif
  1825.             break;
  1826.         }
  1827.  
  1828. /* Output the string */
  1829.  
  1830.         fputs (buf, stdout);
  1831.         }
  1832.     }
  1833.  
  1834. /* Escaped character ? */
  1835.  
  1836.     else if (*s == '\\')
  1837.     {
  1838.         ++s;
  1839.         if ((i = ProcessOutputMetaCharacters (&s)) == -1)
  1840.         i = 0;
  1841.  
  1842.         putchar ((char)i);
  1843.     }
  1844.  
  1845.     else
  1846.         putchar (*(s++));
  1847.     }
  1848.  
  1849.     fflush (stdout);
  1850. }
  1851.  
  1852. /*
  1853.  * Get the current path in UNIX format and save it in the environment
  1854.  * variable $~
  1855.  */
  1856.  
  1857. void GetCurrentDirectory (void)
  1858. {
  1859.     char    ldir[PATH_MAX + 6];
  1860.     char    *CurrentPWDValue;        /* Current directory    */
  1861.  
  1862.     getcwd (ldir, PATH_MAX + 4);
  1863.     ldir[PATH_MAX + 5] = 0;
  1864.  
  1865. /* Convert to Unix format */
  1866.  
  1867. #ifdef OS2
  1868.     PATH_TO_UNIX (ldir);
  1869.  
  1870.     if (!IsHPFSFileSystem (ldir))
  1871.     strlwr (ldir);
  1872. #else
  1873.     PATH_TO_UNIX (strlwr (ldir));
  1874. #endif
  1875.  
  1876. /* Save in environment */
  1877.  
  1878.     SetVariableFromString (tilde, ldir);
  1879.     CurrentDirectory = LookUpVariable (tilde, TRUE);
  1880.  
  1881. /* If we have changed directory, set PWD and OLDPWD */
  1882.  
  1883.     if (strcmp (CurrentPWDValue = GetVariableAsString (PWDVariable, FALSE),
  1884.         ldir))
  1885.     {
  1886.     SetVariableFromString (OldPWDVariable, CurrentPWDValue);
  1887.     SetVariableFromString (PWDVariable, ldir);
  1888.     }
  1889. }
  1890.  
  1891. /*
  1892.  * Initialise the shell and Patch up various parts of the system for the
  1893.  * shell.  At the moment, we modify the ctype table so that _ is an upper
  1894.  * case character.
  1895.  */
  1896.  
  1897. static bool near Initialise (char *name)
  1898. {
  1899.     register char    *s, *s1;
  1900.     char        **ap;
  1901.     bool        OptionsRflag = FALSE;
  1902.  
  1903. /* Patch the ctype table as a cheat */
  1904.  
  1905.     (_ctype+1)['_'] |= _UPPER;
  1906.  
  1907. /* Get original interrupt 24 address and set up our new interrupt 24
  1908.  * address
  1909.  */
  1910.  
  1911. #ifndef OS2
  1912.     Orig_I24_V = _dos_getvect (0x24);
  1913.     _dos_setvect (0x24, SW_Int24);
  1914. #endif
  1915.  
  1916. /* Create the integer variables, in case they are loaded from the
  1917.  * environment
  1918.  */
  1919.     CreateIntegerVariables ();
  1920.  
  1921. /* Load the environment into our structures */
  1922.  
  1923.     if ((ap = environ) != (char **)NULL)
  1924.     {
  1925.     for (ap = environ; *ap != (char *)NULL; ap++)
  1926.     {
  1927.  
  1928. /* Set up any variables.  Note there is an assumption that
  1929.  * AssignVariableFromString sets the equals sign to 0, hiding the value;
  1930.  */
  1931.         if (!strncmp ("SECONDS=", *ap, 8))
  1932.         continue;
  1933.  
  1934.         if (AssignVariableFromString (*ap))
  1935.         SetVariableStatus (*ap, STATUS_EXPORT);
  1936.     }
  1937.     }
  1938.  
  1939. /* Change COMSPEC to unix format for execution */
  1940.  
  1941.     PATH_TO_UNIX (GetVariableAsString (ComspecVariable, FALSE));
  1942.     SetVariableStatus (ComspecVariable, STATUS_CONVERT_MSDOS);
  1943.  
  1944. /* Zap all files */
  1945.  
  1946.     CloseAllHandlers ();
  1947.     MemoryAreaLevel = 1;
  1948.  
  1949. /* Get the current directory */
  1950.  
  1951.     GetCurrentDirectory ();
  1952.  
  1953. /* Initialise the getopts command */
  1954.  
  1955.     ResetGetoptsValues (TRUE);
  1956.  
  1957. /* Set up SHELL variable.  First check for a restricted shell.  Check the
  1958.  * restricted shell
  1959.  */
  1960.  
  1961.     SetVariableFromString (LastWordVariable, name);
  1962.  
  1963.     if ((s = strrchr (name, CHAR_UNIX_DIRECTORY)) == (char *)NULL)
  1964.     s = name;
  1965.  
  1966.     else
  1967.         s++;
  1968.  
  1969.     if ((s1 = strchr (s, '.')) != (char *)NULL)
  1970.     *s1 = 0;
  1971.  
  1972.     if (!strcmp (s, "rsh"))
  1973.     OptionsRflag = TRUE;
  1974.  
  1975. /* Has the program name got a .exe extension - Yes probably DOS 3+.  So
  1976.  * save it as the Shell name
  1977.  */
  1978.  
  1979.     if (s1 != (char *)NULL)
  1980.     {
  1981.     if ((stricmp (s1 + 1, EXEExtension + 1) == 0) &&
  1982.         (GetVariableAsString (ShellVariableName, FALSE) == null))
  1983.         SetVariableFromString (ShellVariableName, name);
  1984.  
  1985.     *s1 = '.';
  1986.     }
  1987.  
  1988. /* Default if necessary */
  1989.  
  1990.     if (GetVariableAsString (ShellVariableName, FALSE) == null)
  1991.     SetVariableFromString (ShellVariableName, shellname);
  1992.  
  1993.     PATH_TO_UNIX (s1 = GetVariableAsString (ShellVariableName, FALSE));
  1994.  
  1995. /* Check for restricted shell */
  1996.  
  1997.     if ((s = strrchr (s1, CHAR_UNIX_DIRECTORY)) == (char *)NULL)
  1998.     s = s1;
  1999.  
  2000.     else
  2001.     s++;
  2002.  
  2003.     if (*s == 'r')
  2004.     OptionsRflag = TRUE;
  2005.  
  2006. /* Set up home directory */
  2007.  
  2008.     if (GetVariableAsString (HomeVariableName, FALSE) == null)
  2009.     {
  2010.         if ((s = GetVariableAsString ("INIT", FALSE)) == null)
  2011.             s = CurrentDirectory->value;
  2012.  
  2013.     SetVariableFromString (HomeVariableName, s);
  2014.     }
  2015.  
  2016. /* Set up OS Mode */
  2017.  
  2018.     SetVariableFromNumeric (LIT_OSmode, (long)_osmode);
  2019.  
  2020. /* Set up history file location */
  2021.  
  2022.     SetVariableFromNumeric (LIT_Dollar, (long)getpid ());
  2023.  
  2024.     LoadGlobalVariableList ();
  2025.     PATH_TO_UNIX (GetVariableAsString (PathLiteral, FALSE));
  2026.  
  2027.     return OptionsRflag;
  2028. }
  2029.  
  2030. /*
  2031.  * Mail Check processing.  Every $MAILCHECK seconds, we check either $MAIL
  2032.  * or $MAILPATH to see if any file has changed its modification time since
  2033.  * we last looked.  In $MAILCHECK, the files are separated by semi-colon (;).
  2034.  * If the filename contains a %, the string following the % is the message
  2035.  * to display if the file has changed.
  2036.  */
  2037.  
  2038. static void near CheckForMailArriving (void)
  2039. {
  2040.     int            delay = (int)GetVariableAsNumeric (MailCheckVariable);
  2041.     char        *mail = GetVariableAsString ("MAIL", FALSE);
  2042.     char        *mailp = GetVariableAsString ("MAILPATH", FALSE);
  2043.     static time_t    last = 0L;
  2044.     time_t        current = time ((time_t *)NULL);
  2045.     struct stat        st;
  2046.     char        *cp, *sp, *ap;
  2047.  
  2048. /* Have we waited long enough */
  2049.  
  2050.     if (((current - last) < delay) || (DisabledVariables & DISABLE_MAILCHECK))
  2051.     return;
  2052.  
  2053. /* Yes - Check $MAILPATH.  If it is defined, process it.  Otherwise, use
  2054.  * $MAIL
  2055.  */
  2056.  
  2057.     if (mailp != null)
  2058.     {
  2059.  
  2060. /* Check MAILPATH */
  2061.  
  2062.     sp = mailp;
  2063.  
  2064. /* Look for the next separator */
  2065.  
  2066.     while ((cp = strchr (sp, ';')) != (char *)NULL)
  2067.     {
  2068.         *cp = 0;
  2069.  
  2070. /* % in string ? */
  2071.  
  2072.         if ((ap = strchr (ap, '%')) != (char *)NULL)
  2073.         *ap = 0;
  2074.  
  2075. /* Check the file name */
  2076.  
  2077.         if ((stat (sp, &st) != -1) && (st.st_mtime > last) && st.st_size)
  2078.         {
  2079.         if (ap != (char *)NULL)
  2080.             fprintf (stderr, "%s\n", ap + 1);
  2081.  
  2082.         else
  2083.             fputs (ymail, stderr);
  2084.         }
  2085.  
  2086. /* Restore the % */
  2087.  
  2088.         if (ap != (char *)NULL)
  2089.         *ap = '%';
  2090.  
  2091. /* Restore the semi-colon and find the next one */
  2092.  
  2093.         *cp = ';';
  2094.         sp = cp + 1;
  2095.     }
  2096.     }
  2097.  
  2098. /* Just check MAIL */
  2099.  
  2100.     else if ((mail != null) && (stat (mail, &st) != -1) &&
  2101.          (st.st_mtime > last) && st.st_size)
  2102.     fputs (ymail, stderr);
  2103.  
  2104. /* Save the last check time */
  2105.  
  2106.     last = current;
  2107. }
  2108.  
  2109. /*
  2110.  * Preprocess Argv to get handle of options in the format /x
  2111.  *
  2112.  * Some programs invoke the shell using / instead of - to mark the options.
  2113.  * We need to convert to -.  Also /c is a special case.  The rest of the
  2114.  * command line is the command to execute.  So, we get the command line
  2115.  * from the original buffer instead of argv array.
  2116.  */
  2117.  
  2118. static void near Pre_Process_Argv (char **argv, int *argc1)
  2119. {
  2120. #ifdef OS2
  2121.     char    *ocl = (char far *)((((long)_aenvseg) << 16) + _acmdln);
  2122.     int        argc = 1;
  2123.  
  2124.     ocl += strlen (ocl) + 1;
  2125. #else
  2126.     char    *ocl = (char far *)((((long)_psp) << 16) + 0x081L);
  2127.     int        argc = 1;
  2128. #endif
  2129.  
  2130. /* Check for these options */
  2131.  
  2132.     while ((*++argv != (char *)NULL) && (strlen (*argv) == 2) &&
  2133.        (**argv == CHAR_UNIX_DIRECTORY))
  2134.     {
  2135.     argc++;
  2136.     *strlwr (*argv) = '-';
  2137.  
  2138. /* Get the original information from the command line */
  2139.  
  2140.     if ((*argv)[1] == 'c')
  2141.     {
  2142.         while ((*ocl != CHAR_UNIX_DIRECTORY) && (*(ocl + 1) != 'c') &&
  2143.            (*ocl) && (*ocl != CHAR_RETURN))
  2144.         ++ocl;
  2145.  
  2146.         if (*ocl != CHAR_UNIX_DIRECTORY)
  2147.         continue;
  2148.  
  2149. /* Find the start of the string */
  2150.  
  2151.         ocl += 2;
  2152.  
  2153.         while (isspace (*ocl) && (*ocl != CHAR_RETURN))
  2154.         ++ocl;
  2155.  
  2156.         if (*ocl == CHAR_RETURN)
  2157.         continue;
  2158.  
  2159. /* Found the start.  Set up next parameter and ignore the rest */
  2160.  
  2161.         if (*(argv + 1) == (char *)NULL)
  2162.         continue;
  2163.  
  2164.         argc++;
  2165.         *(argv + 1) = ocl;
  2166.         *(argv + 2) = (char *)NULL;
  2167.         *argc1 = argc;
  2168.  
  2169.         if ((ocl = strchr (ocl, CHAR_RETURN)) != (char *)NULL)
  2170.         *ocl = 0;
  2171.  
  2172.         return;
  2173.     }
  2174.     }
  2175. }
  2176.  
  2177. /*
  2178.  * Convert path format to/from UNIX
  2179.  */
  2180.  
  2181. char *ConvertPathToFormat (char *path, char in, char out)
  2182. {
  2183.     char    *s = path;
  2184.  
  2185.     while ((path = strchr (path, in)) != (char *)NULL)
  2186.     *path = out;
  2187.  
  2188.     return s;
  2189. }
  2190.  
  2191. /* Load profiles onto I/O Stack */
  2192.  
  2193. static void near LoadTheProfileFiles (void)
  2194. {
  2195.     char    *name = BuildFileName ("profile"); /* Set up home profile */
  2196.     char    *Pname = "x:/etc/profile";
  2197.  
  2198.     InteractiveFlag = TRUE;
  2199.     AddToStackForExection (name);
  2200.     ReleaseMemoryCell ((void *)name);
  2201.     *Pname = (char)(GetRootDiskDrive () + 'a' - 1);
  2202.     AddToStackForExection (Pname);
  2203. }
  2204.  
  2205. /*
  2206.  * Convert Unix PATH to MSDOS PATH
  2207.  */
  2208.  
  2209. static void near ConvertUnixPathToMSDOS (void)
  2210. {
  2211.     char    *cp = GetVariableAsString (PathLiteral, FALSE);
  2212.     char    *scp = cp;
  2213.     int        colon = 0;
  2214.  
  2215. /* If there is a semi-colon or a backslash, we assume this is a DOS format
  2216.  * path
  2217.  */
  2218.  
  2219.     if ((strchr (cp, ';') != (char *)NULL) ||
  2220.     (strchr (cp, '\\') != (char *)NULL))
  2221.     return;
  2222.  
  2223. /* Count the number of colons */
  2224.  
  2225.     while ((cp = strchr (cp, ':')) != (char *)NULL)
  2226.     {
  2227.     ++colon;
  2228.     ++cp;
  2229.     }
  2230.  
  2231. /* If there are no colons or there is one colon as the second character, it
  2232.  * is probably an MSDOS path
  2233.  */
  2234.  
  2235.     cp = scp;
  2236.     if ((colon == 0) || ((colon == 1) && (*(cp + 1) == ':')))
  2237.     return;
  2238.  
  2239. /* Otherwise, convert all colons to semis */
  2240.  
  2241.     while ((cp = strchr (cp, ':')) != (char *)NULL)
  2242.     *(cp++) = ';';
  2243. }
  2244.  
  2245. /* Generate a file name from a directory and name.  Return null if an error
  2246.  * occurs or some malloced space containing the file name otherwise
  2247.  */
  2248.  
  2249. char *BuildFileName (char *name)
  2250. {
  2251.     char    *dir = GetVariableAsString (HomeVariableName, FALSE);
  2252.     char    *cp;
  2253.  
  2254. /* Get some space */
  2255.  
  2256.     if ((cp = AllocateMemoryCell (strlen (dir) + strlen (name) + 2))
  2257.         == (char *)NULL)
  2258.     return null;
  2259.  
  2260. /* Addend the directory and a / if the directory does not end in one */
  2261.  
  2262.     strcpy (cp, dir);
  2263.  
  2264.     if (cp[strlen (cp) - 1] != CHAR_UNIX_DIRECTORY)
  2265.     strcat (cp, DirectorySeparator);
  2266.  
  2267. /* Append the file name */
  2268.  
  2269.     return strcat (cp, name);
  2270. }
  2271.  
  2272. /* Clear prompts */
  2273.  
  2274. static void near ClearUserPrompts (void)
  2275. {
  2276.     ClearVariableStatus (PS1, STATUS_EXPORT);
  2277.     ClearVariableStatus (PS2, STATUS_EXPORT);
  2278.     ClearVariableStatus (PS3, STATUS_EXPORT);
  2279.     SetVariableFromString (PS1, null);
  2280.     SetVariableFromString (PS2, null);
  2281.     SetVariableFromString (PS3, null);
  2282. }
  2283.  
  2284. /* Process setting of SECONDS and RANDOM environment variables */
  2285.  
  2286. static void near SecondAndRandomEV (char *name, long val)
  2287. {
  2288.     if (!strcmp (name, SecondsVariable) &&
  2289.     !(DisabledVariables & DISABLE_SECONDS))
  2290.     {
  2291.     SecondsOffset = (time_t)val;
  2292.     ShellStartTime = time ((time_t *)NULL);
  2293.     }
  2294.  
  2295.     else if (!strcmp (name, RandomVariable) &&
  2296.          !(DisabledVariables & DISABLE_RANDOM))
  2297.     srand ((int)val);
  2298. }
  2299.  
  2300. /*
  2301.  * Set up the Window name.  Give up if it does not work.
  2302.  */
  2303.  
  2304. #ifdef OS2
  2305. void SetWindowName (void)
  2306. {
  2307.     HSWITCH        hswitch;
  2308.     SWCNTRL        swctl;
  2309.     PIDINFO        PidInfo;
  2310.  
  2311.     if (DosGetPID (&PidInfo))
  2312.     return;
  2313.  
  2314.     if (!(hswitch = WinQuerySwitchHandle (0, PidInfo.pid)))
  2315.     return;
  2316.  
  2317.     if (WinQuerySwitchEntry (hswitch, &swctl))
  2318.     return;
  2319.  
  2320.     strcpy (swctl.szSwtitle, "MS Shell");
  2321.     WinChangeSwitchEntry (hswitch, &swctl);
  2322. }
  2323.  
  2324. /*
  2325.  * In OS/2, check for terminated processes
  2326.  */
  2327.  
  2328. static void near CheckForTerminatedProcess (void)
  2329. {
  2330.     RESULTCODES    rescResults;
  2331.     PID        pidProcess;
  2332.     char    *s;
  2333.  
  2334.     while (TRUE)
  2335.     {
  2336.     if (DosCwait (DCWA_PROCESSTREE, DCWW_NOWAIT, &rescResults,
  2337.               &pidProcess, 0))
  2338.         return;
  2339.  
  2340.     DeleteJob (pidProcess);
  2341.  
  2342.     switch (rescResults.codeTerminate)
  2343.     {
  2344.         case TC_EXIT:
  2345.         s = "Normal Exit";
  2346.         break;
  2347.  
  2348.         case TC_HARDERROR:
  2349.         s = "Hard error";
  2350.         break;
  2351.  
  2352.         case TC_TRAP:
  2353.         s = "Trapped";
  2354.         break;
  2355.  
  2356.         case TC_KILLPROCESS:
  2357.         s = "Killed";
  2358.         break;
  2359.  
  2360.         default:
  2361.         s = "Unknown";
  2362.         break;
  2363.  
  2364.     }
  2365.  
  2366.     fprintf (stderr, "Process %d terminated - %s (%d)\n", pidProcess, s,
  2367.          rescResults.codeTerminate);
  2368.     }
  2369. }
  2370. #endif
  2371.  
  2372. /*
  2373.  * Set up the Parameter Environment variables
  2374.  */
  2375.  
  2376. static void near SetUpParameterEV (int argc, char **argv, char *name)
  2377. {
  2378.     Word_B    *wb = (Word_B *)NULL;
  2379.     char    *Value;
  2380.     int        i;
  2381.  
  2382.     if ((Value = StringSave (name)) == null)
  2383.     {
  2384.     fprintf (stderr, BasicErrorMessage, ShellNameLiteral, Outofmemory1);
  2385.     return;
  2386.     }
  2387.  
  2388.     wb = AddParameter (Value, wb, ShellNameLiteral);
  2389.  
  2390.     for (i = 1; i < argc; ++i)
  2391.     {
  2392.     if ((!AssignVariableFromString (argv[i])) && (wb != (Word_B *)NULL))
  2393.     {
  2394.         if ((Value = StringSave (argv[i])) != null)
  2395.         wb = AddParameter (Value, wb, ShellNameLiteral);
  2396.  
  2397.         else
  2398.         {
  2399.         fprintf (stderr, BasicErrorMessage, ShellNameLiteral,
  2400.              Outofmemory1);
  2401.         return;
  2402.         }
  2403.     }
  2404.     }
  2405.  
  2406.     if (wb != (Word_B *)NULL)
  2407.     wb = AddParameter ((char *)NULL, wb, ShellNameLiteral);
  2408. }
  2409.  
  2410. /*
  2411.  * Update the Seconds and Random variables
  2412.  */
  2413.  
  2414. void HandleSECONDandRANDOM (void)
  2415. {
  2416.     if (!(DisabledVariables & DISABLE_SECONDS))
  2417.     LookUpVariable (SecondsVariable, TRUE);
  2418.  
  2419.     if (!(DisabledVariables & DISABLE_RANDOM))
  2420.     LookUpVariable (RandomVariable, TRUE);
  2421. }
  2422.  
  2423. /*
  2424.  * Set the status of an environment variable
  2425.  */
  2426.  
  2427. void SetVariableStatus (char *name, int flag)
  2428. {
  2429.     VariableList    *vp = LookUpVariable (name, TRUE);
  2430.  
  2431.     if (isalpha (*vp->name))        /* not an internal symbol ($# etc) */
  2432.     vp->status |= flag;
  2433. }
  2434.  
  2435. /*
  2436.  * Set the status of an environment variable
  2437.  */
  2438.  
  2439. void ClearVariableStatus (char *name, int flag)
  2440. {
  2441.     VariableList    *vp = LookUpVariable (name, TRUE);
  2442.  
  2443.     if (isalpha (*vp->name))        /* not an internal symbol ($# etc) */
  2444.     vp->status &= ~flag;
  2445. }
  2446.  
  2447. /*
  2448.  * Check allowed to set variable
  2449.  */
  2450.  
  2451. static bool near AllowedToSetVariable (VariableList *vp)
  2452. {
  2453.     if (vp->status & STATUS_READONLY)
  2454.     {
  2455.     ShellErrorMessage ("%s is read-only", vp->name);
  2456.     return FALSE;
  2457.     }
  2458.  
  2459. /* Check for $PATH, $SHELL or $ENV reset in restricted shell */
  2460.  
  2461.     if ((!strcmp (vp->name, PathLiteral) || !strcmp (vp->name, ENVVariable) ||
  2462.      !strcmp (vp->name, ShellVariableName)) &&
  2463.     CheckForRestrictedShell (PathLiteral))
  2464.     return FALSE;
  2465.  
  2466.     return TRUE;
  2467. }
  2468.  
  2469. /*
  2470.  * Set up a variable from a string
  2471.  */
  2472.  
  2473. void SetVariableFromString (char *name, char *val)
  2474. {
  2475.     VariableList    *vp = LookUpVariable (name, TRUE);
  2476.     char        *xp = null;
  2477.     long        nval;
  2478.  
  2479. /* Check if allowed to set variable */
  2480.  
  2481.     if (!AllowedToSetVariable (vp))
  2482.     return;
  2483.  
  2484. /* If we change the PATH to a new value, we need to untrack some aliases */
  2485.  
  2486.     if (!strcmp (name, PathLiteral) && strcmp (vp->value, val))
  2487.     UnTrackAllAliases ();
  2488.  
  2489.     CheckOPTIND (name, atol (val));
  2490.  
  2491. /* Save the new value */
  2492.  
  2493.     if ((!(vp->status & STATUS_INTEGER)) && (val != null) && strlen (val) &&
  2494.     ((xp = StringSave (val = SuppressSpacesZeros (vp, val))) == null))
  2495.         return;
  2496.  
  2497. /* Free the old value if appropriate */
  2498.  
  2499.     if (vp->value != null)
  2500.     ReleaseMemoryCell ((void *)vp->value);
  2501.  
  2502.     vp->value = null;
  2503.  
  2504. /* String value? */
  2505.  
  2506.     if (!(vp->status & STATUS_INTEGER))
  2507.     {
  2508.     vp->value = xp;
  2509.  
  2510.     if (!vp->width)
  2511.         vp->width = strlen (val);
  2512.     }
  2513.  
  2514. /* No - Number value */
  2515.  
  2516.     else if (!ValidMathsExpression (val, &nval))
  2517.     SetUpANumericValue (vp, nval, -1);
  2518.  
  2519. /* Check to see if it should be exported */
  2520.  
  2521.     if (FL_TEST ('a'))
  2522.     vp->status |= STATUS_EXPORT;
  2523.  
  2524. /* Convert UNIX to DOS for PATH variable */
  2525.  
  2526.     if (!strcmp (name, PathLiteral))
  2527.     ConvertUnixPathToMSDOS ();
  2528. }
  2529.  
  2530. /*
  2531.  * Set a variable from a numeric
  2532.  */
  2533.  
  2534. void        SetVariableFromNumeric (char *name, long value)
  2535. {
  2536.     VariableList    *vp = LookUpVariable (name, TRUE);
  2537.     char        NumericBuffer[20];
  2538.  
  2539. /* Check if allowed to set variable */
  2540.  
  2541.     if (!AllowedToSetVariable (vp))
  2542.     return;
  2543.  
  2544.     CheckOPTIND (name, value);
  2545.  
  2546.     if (!(vp->status & STATUS_INTEGER))
  2547.     {
  2548.     sprintf (NumericBuffer, "%ld", value);
  2549.     SetVariableFromString (name, NumericBuffer);
  2550.     }
  2551.  
  2552. /* Save the integer value */
  2553.  
  2554.     else
  2555.     SetUpANumericValue (vp, value, -1);
  2556. }
  2557.  
  2558. /*
  2559.  * Get variable as a numeric
  2560.  */
  2561.  
  2562. long        GetVariableAsNumeric (char *name)
  2563. {
  2564.     VariableList    *vp = LookUpVariable (name, FALSE);
  2565.  
  2566.     if (vp->status & STATUS_INTEGER)
  2567.     return vp->nvalue;
  2568.  
  2569.     else
  2570.     return atol (vp->value);
  2571. }
  2572.  
  2573. /*
  2574.  * Get variable as a numeric
  2575.  */
  2576.  
  2577. char        *GetVariableAsString (char *name, bool Format)
  2578. {
  2579.     VariableList    *vp = LookUpVariable (name, FALSE);
  2580.     char        *Value = vp->value;
  2581.     char        *xp;
  2582.     size_t        len;
  2583.     char        *NumericBuffer;
  2584.  
  2585.     if (vp->status & STATUS_INTEGER)
  2586.     {
  2587.     if ((NumericBuffer = GetAllocatedSpace (40)) == (char *)NULL)
  2588.         return null;
  2589.  
  2590.     if (vp->base != 10)
  2591.     {
  2592.         sprintf (NumericBuffer, "[%d]", vp->base);
  2593.         xp = NumericBuffer + strlen (NumericBuffer);
  2594.     }
  2595.  
  2596.     else
  2597.         xp = NumericBuffer;
  2598.  
  2599.         ltoa (vp->nvalue, xp, vp->base);
  2600.     return NumericBuffer;
  2601.     }
  2602.  
  2603. /* Handle a string variable, if no formating required, return it */
  2604.  
  2605.     if (!Format)
  2606.     return vp->value;
  2607.  
  2608. /* Left justify ? */
  2609.  
  2610.     if (vp->status & STATUS_LEFT_JUSTIFY)
  2611.     {
  2612.     xp = SuppressSpacesZeros (vp, Value);
  2613.  
  2614.     if ((Value = GetAllocatedSpace (vp->width + 1)) == (char *)NULL)
  2615.         return null;
  2616.  
  2617.     memset (Value, ' ', vp->width);
  2618.     Value[vp->width] = '\0';
  2619.  
  2620.     if ((len = strlen (xp)) > vp->width)
  2621.         len = vp->width;
  2622.  
  2623.     memcpy (Value, xp, len);
  2624.     }
  2625.  
  2626. /* Right justify ? */
  2627.  
  2628.     else if (vp->status & (STATUS_RIGHT_JUSTIFY | STATUS_ZERO_FILL))
  2629.     {
  2630.     if ((xp = GetAllocatedSpace (vp->width + 1)) == (char *)NULL)
  2631.         return null;
  2632.  
  2633.     if ((len = strlen (Value)) < vp->width)
  2634.     {
  2635.         memset (xp,
  2636.             ((vp->status & STATUS_ZERO_FILL) &&
  2637.              (isdigit (*Value))) ? '0' : ' ',
  2638.             vp->width);
  2639.  
  2640.         memcpy (xp + (vp->width - len), Value, len);
  2641.     }
  2642.  
  2643.     else
  2644.         memcpy (xp, Value + vp->width - len, vp->width);
  2645.  
  2646.     *(xp + vp->width) = 0;
  2647.     Value = xp;
  2648.     }
  2649.  
  2650. /* Handle upper and lower case conversions */
  2651.  
  2652.     if (vp->status & STATUS_LOWER_CASE)
  2653.     Value = strlwr (StringCopy (Value));
  2654.  
  2655.     if (vp->status & STATUS_UPPER_CASE)
  2656.     Value = strupr (StringCopy (Value));
  2657.  
  2658.     return Value;
  2659. }
  2660.  
  2661. /*
  2662.  * Set up a numeric value
  2663.  */
  2664.  
  2665. static void near SetUpANumericValue (VariableList *vp, long value, int base)
  2666. {
  2667.     vp->nvalue = value;
  2668.     vp->status |= STATUS_INTEGER;
  2669.  
  2670.     if (vp->base == 0)
  2671.     vp->base = (base > 1) ? base
  2672.                   : ((LastNumberBase != -1) ? LastNumberBase
  2673.                             : 10);
  2674.  
  2675.     if (vp->value != null)
  2676.     ReleaseMemoryCell ((void *)vp->value);
  2677.  
  2678.     vp->value = null;
  2679. }
  2680.  
  2681. /*
  2682.  * Suppress leading spaces and zeros
  2683.  */
  2684.  
  2685. static char * near SuppressSpacesZeros (VariableList *vp, char *value)
  2686. {
  2687. /* Suppress blanks and zeros */
  2688.  
  2689.     if (vp->status & STATUS_LEFT_JUSTIFY)
  2690.     {
  2691.     while (*value == ' ')
  2692.         value++;
  2693.  
  2694.     if (vp->status & STATUS_ZERO_FILL)
  2695.     {
  2696.         while (*value == '0')
  2697.         value++;
  2698.     }
  2699.     }
  2700.  
  2701.     return value;
  2702. }
  2703.  
  2704. /*
  2705.  * Check to see if a reset of CheckOPTIND has occured
  2706.  */
  2707.  
  2708. static void near CheckOPTIND (char *name, long value)
  2709. {
  2710.     if ((value == 1) && (!(DisabledVariables & DISABLE_OPTIND)) &&
  2711.     (strcmp (OptIndVariable, name) == 0))
  2712.     ResetGetoptsValues (FALSE);
  2713. }
  2714.  
  2715. /*
  2716.  * Initialise the Integer variables by creating them
  2717.  */
  2718.  
  2719. static void near CreateIntegerVariables (void)
  2720. {
  2721.     struct ShellVariablesInit    *wp = InitialiseShellVariables;
  2722.  
  2723.     while  (wp->Name != (char *)NULL)
  2724.     {
  2725.     SetVariableStatus (wp->Name, wp->Status);
  2726.  
  2727.     if (wp->CValue != null)
  2728.         SetVariableFromString (wp->Name, wp->CValue);
  2729.  
  2730.     wp++;
  2731.     }
  2732. }
  2733.